﻿using Microsoft.Xna.Framework;

namespace Bunbyx.game
{
    // --------------------------------------------------------------------
    //  Enumerations
    // --------------------------------------------------------------------

    public enum Action
    {
        GoLeft,
        GoRight,
        GoUp,
        GoDown,
        Stop,
        SlideR,
        SlideL
    }

    // --------------------------------------------------------------------
    //  Bunbyx
    // --------------------------------------------------------------------
    
    public class BunbyX : Sprite
    {
        // --------------------------------------------------------------------
        //  Constants
        // --------------------------------------------------------------------

        private const int MOVE_UP = -1;
        private const int MOVE_DOWN = 1;
        private const int MOVE_LEFT = -1;
        private const int MOVE_RIGHT = 1;

        private const int BASE_SPEED = 350;
        private const int BASE_JUMP = 500;

        private const float SPEED_UP_VALUE = 1.1f;
        private const float JUMP_UP_VALUE = 1.1f;

        private const int SPRITE_WIDTH = 171;
        private const int SPRITE_HEIGHT = 260;

        private const int NB_SPRITE_STILL = 4;
        private const int NB_SPRITE_RUNNING = 6;
        private const int NB_SPRITE_JUMPING = 1;
        private const int NB_SPRITE_FALLING = 1;
        private const int NB_SPRITE_WALL_GRIPPING = 1;

        private const int LINE_SPRITE_STILL = 0;
        private const int LINE_SPRITE_RUNNING = 1;
        private const int LINE_SPRITE_JUMPING = 2;
        private const int LINE_SPRITE_FALLING = 2;
        private const int LINE_SPRITE_WALL_GRIPPING = 2;

        private const int START_SPRITE_STILL = 0;
        private const int START_SPRITE_RUNNING = 0;
        private const int START_SPRITE_JUMPING = 0;
        private const int START_SPRITE_FALLING = 2;
        private const int START_SPRITE_WALL_GRIPPING = 4;

        private const int SPRITE_FRAME_RATE = 5;
        private const int SPRITE_FRAME_RATE_RUNNING = 5;
        private const int SPRITE_FRAME_RATE_STILL = 5;

        // --------------------------------------------------------------------
        //  Private enumerations
        // --------------------------------------------------------------------

        private enum State
        {
            Waiting,
            Running,
            Jumping,
            Crounching,
            Sliding
        }

        // --------------------------------------------------------------------
        //  Private attributes
        // --------------------------------------------------------------------

        private bool blockedLeft = false;
        private bool blockedRight = false;
        private bool blockedTop = false;
        private bool blockedBot = false;

        private int strength;
        private int intelligence;
        private int agility;
        private int constitution;

        private int healthPoint;
        private int maxHealthPoint;

        private int manaPoint;
        private int maxManaPoint;

        private float speedCoef;
        private float jumpCoef;
        private State state;
        private Vector2 direction;
        private Vector2 speed;
        private SoundPlayer soundPlayer;

        private Action lastAction;
        private Vector2 lastPosition;
        private int spriteCounter;
        private int spriteFrameSpeedCounter;
        private const int COEF_AIR_REVERSE = 50;
        private bool superBunbyxWJ = false;

        // --------------------------------------------------------------------
        //  Constructor
        // --------------------------------------------------------------------

        public BunbyX(Vector2 position, SoundPlayer soundPlayer)
            : base(SpriteType.Bunbyx, position)
        {
            this.soundPlayer = soundPlayer;
            this.Scale = 0.5f;
            
            speedCoef = 1;
            jumpCoef = 1;
            state = State.Waiting;
            direction = Vector2.Zero;
            speed = Vector2.Zero;

            direction.X = MOVE_RIGHT;
            lastAction = Action.Stop;
            lastPosition = Vector2.Zero;
            UpdateSprite();
        }

        // --------------------------------------------------------------------
        //  Public methods
        // --------------------------------------------------------------------

        public void PreUpdate()
        {
            switch (state) {
                case (State.Waiting):
                    {
                        
                        break;
                    }
                case (State.Running):
                    {
                        break;
                    }
                case (State.Jumping):
                    {
                        if (speed.X >= BASE_SPEED)
                        {
                            speed.X = BASE_SPEED;
                        }
                        if (!blockedTop)
                        {
                            direction.Y = MOVE_UP;
                            speed.Y -= 15;
                        }
                        else if (blockedTop && !blockedBot)
                        {
                            direction.Y = MOVE_UP;
                            if (speed.Y < 0)
                            {
                                speed.Y -= 15;
                            }
                            else
                            {
                                speed.Y = -15;
                            }
                        }
                        break;
                    }
                case (State.Crounching):
                    {
                        speed.X = 0;
                        break;
                    }
                case (State.Sliding):
                    {
                        if (speed.X >= 50)
                        {
                            speed.X -= 50;
                        }
                        else
                        {
                            state = State.Crounching;
                            speed.X = 0;
                        }
                        break;
                    }
            }
        }

        public override void Update(GameTime gameTime)
        {
            float elapsedTime = (float)(gameTime.ElapsedGameTime.TotalMilliseconds * 0.001);
            
            Position += direction * speed * elapsedTime;
        }

        public void LoadLastPosition()
        {
            Position=lastPosition;
        }


        public void DoAction(Action action)
        {
            if (action != lastAction) 
            {
                spriteCounter = 0;
                spriteFrameSpeedCounter = 0;
                lastAction = action;
            }
            switch (state) {
                case (State.Waiting):
                    {
                        switch (action)
                        {
                            case (Action.GoLeft): { GoLeft_Ground(); break; }
                            case (Action.GoRight): { GoRight_Ground(); break; }
                            case (Action.GoUp): { Jump_Ground(); break; }
                            case (Action.GoDown): { Crouch(); break; }
                            case (Action.Stop): { speed = Vector2.Zero; ;state = State.Waiting; break; }
                        } 
                        break;
                    }
                case (State.Running):
                    {
                        switch (action)
                        {
                            case (Action.GoLeft): { GoLeft_Ground(); break; }
                            case (Action.GoRight): { GoRight_Ground(); break; }
                            case (Action.SlideL): { Crouch(); Slide_Left_Init(); break; }
                            case (Action.SlideR): { Crouch(); Slide_Right_Init(); break; }
                            case (Action.GoUp): { Jump_Ground(); break; }
                            case (Action.GoDown): { Crouch(); break; }
                            case (Action.Stop): { speed = Vector2.Zero; ;state = State.Waiting; break; }
                        } 
                        break;
                    }
                case (State.Jumping):
                    {
                        switch (action)
                        {
                            case (Action.GoLeft): { GoLeft_Air(); break; }
                            case (Action.GoRight): { GoRight_Air(); break; }
                            case (Action.GoUp): { Jump_Air(); break; }
                            case (Action.Stop): { break; }
                        } 
                        break;
                    }
                case (State.Crounching):
                    {
                        switch (action)
                        {
                            case (Action.SlideL): { Slide_Left_Init(); break; }
                            case (Action.SlideR): { Slide_Right_Init(); break; }
                            case (Action.GoLeft): { StandingUp();  break; }
                            case (Action.GoRight): { StandingUp();  break; }
                            case (Action.GoUp): { break; }
                            case (Action.GoDown): { break; }
                            case (Action.Stop): { StandingUp(); break; }
                        }
                        break;
                    }
            }    
            lastPosition = Position;
            UpdateSprite();
        }

        public void SetBlockedLeft(bool state)
        {
            blockedLeft = state;
        }

        public void SetBlockedRight(bool state)
        {
            blockedRight = state;
        }

        public void SetBlockedTop(bool state)
        {
            blockedTop = state;
        }

        public void SetBlockedBot(bool state)
        {
            blockedBot = state;
        }

        public void OnTheGround()
        {
            if (state == State.Jumping)
            {
                state = State.Waiting;
            }
        }

        public void InTheAir()
        {
            if (state == State.Sliding)
            {
                LoadLastPosition();
                speed = Vector2.Zero;
                StandingUp();
            }
            else
            {
                state = State.Jumping;
            }
        }

        private void Crouch()
        {
            if (state != State.Crounching)
            {
                Position.Y += (SPRITE_HEIGHT * Scale) / 2;
                state = State.Crounching;
                UpdateSprite();
            }
        }

        public void StandingUp()
        {
            Position.Y -= (SPRITE_HEIGHT*Scale)/2;
            state = State.Waiting; 
        }

        public void SpeedUp()
        {
            speedCoef *= SPEED_UP_VALUE;
        }

        public void JumpUp()
        {
            jumpCoef *= JUMP_UP_VALUE;
        }
        public void SuperBunbyxWJ() 
        {
            superBunbyxWJ = true; 
        }

        //Movement Methods
        //Ground
        private void GoLeft_Ground()
        {
            if (blockedLeft)
            {
                speed.X = 0;
                state = State.Waiting;
            }
            else
            {
                state = State.Running;

                if (speed.X < BASE_SPEED && direction.X == MOVE_LEFT)
                {
                    speed.X += BASE_SPEED / 10;
                }
                else if (direction.X != MOVE_LEFT) 
                {
                    direction.X = MOVE_LEFT;
                    speed.X = 0;
                }
            }
        }
        private void GoRight_Ground()
        {
            if (blockedRight)
            {
                speed.X = 0;
                state = State.Waiting;
            }
            else
            {
                state = State.Running;

                if (speed.X < BASE_SPEED && direction.X == MOVE_RIGHT)
                {
                    speed.X += BASE_SPEED / 10;
                }
                else if (direction.X != MOVE_RIGHT)
                {
                    direction.X = MOVE_RIGHT;
                    speed.X = 0;
                }
            }
        }
        private void Jump_Ground() 
        {
            state = State.Jumping;
            direction.Y = MOVE_UP;
            speed = new Vector2(speed.X, BASE_JUMP * jumpCoef);
            soundPlayer.PlaySound(Sound.Jump);
        }
       
        private void Slide_Right_Init() 
        {
            if (state == State.Running || speed.X == 0)
            {
                state = State.Sliding;
                direction.X = MOVE_RIGHT;
                speed.X = BASE_SPEED*4 ;
            }
        }
        private void Slide_Left_Init()
        {
            if (speed.X == 0)
            {
                state = State.Sliding;
                direction.X = MOVE_LEFT;
                speed.X = BASE_SPEED*4;
            }
        }

        //Air
        private void GoLeft_Air()
        {
            if (blockedLeft)
            {
                speed.X = 0;
            }
            else
            {
                if (direction.X == MOVE_RIGHT)
                {
                    if (speed.X >= 0)
                    {
                        speed.X -= (BASE_SPEED / COEF_AIR_REVERSE) * speedCoef;
                    }
                }
                else if (direction.X == MOVE_LEFT)
                {
                    if (speed.X <= BASE_SPEED * speedCoef)
                    {
                        speed.X += (BASE_SPEED / COEF_AIR_REVERSE) * speedCoef;
                    }
                }

                if (speed.X <= (BASE_SPEED / COEF_AIR_REVERSE) * speedCoef)
                {
                    direction.X = MOVE_LEFT;
                }
            }
        }
        private void GoRight_Air()
        {
            if (blockedRight)
            {
                speed.X = 0;
            }
            else
            {
                if (direction.X == MOVE_RIGHT)
                {
                    if (speed.X <= BASE_SPEED * speedCoef)
                    {
                        speed.X += (BASE_SPEED / COEF_AIR_REVERSE) * speedCoef;
                    }
                }
                else if (direction.X == MOVE_LEFT)
                {
                    if (speed.X >= 0)
                    {
                        speed.X -= (BASE_SPEED / COEF_AIR_REVERSE) * speedCoef;
                    }
                }

                if (speed.X <= (BASE_SPEED / COEF_AIR_REVERSE) * speedCoef)
                {
                    direction.X = MOVE_RIGHT;
                }
            }
        }
        private void Jump_Air()
        {
            if (superBunbyxWJ)
            {
                if (blockedLeft)
                {
                    direction.Y = MOVE_UP;
                    direction.X = MOVE_RIGHT;
                    speed = new Vector2(BASE_SPEED, BASE_JUMP * jumpCoef);
                    soundPlayer.PlaySound(Sound.Jump);
                }
                else if (blockedRight)
                {
                    direction.Y = MOVE_UP;
                    direction.X = MOVE_LEFT;
                    speed = new Vector2(BASE_SPEED, BASE_JUMP * jumpCoef);
                    soundPlayer.PlaySound(Sound.Jump);
                }
            }
        }


        private void UpdateSprite() 
        {
            spriteFrameSpeedCounter++;
            if (spriteFrameSpeedCounter >= SPRITE_FRAME_RATE_STILL)
            {
                spriteFrameSpeedCounter = 0;
                spriteCounter++;
                if (spriteCounter >= NB_SPRITE_STILL)
                {
                    spriteCounter = 0;
                }
            }

            switch (state) 
            {
                case (State.Waiting): 
                    {                        
                        if (direction.X == MOVE_LEFT) { Source = new Rectangle((SPRITE_WIDTH * START_SPRITE_STILL) + (SPRITE_WIDTH * spriteCounter), SPRITE_HEIGHT * LINE_SPRITE_STILL, SPRITE_WIDTH, SPRITE_HEIGHT); }
                        if (direction.X == MOVE_RIGHT) { Source = new Rectangle((SPRITE_WIDTH * START_SPRITE_STILL) + (SPRITE_WIDTH * spriteCounter) + (SPRITE_WIDTH * NB_SPRITE_STILL), SPRITE_HEIGHT * LINE_SPRITE_STILL, SPRITE_WIDTH, SPRITE_HEIGHT); }
                        break;
                    }
                case (State.Running):
                    {
                        if (direction.X == MOVE_LEFT) { Source = new Rectangle(SPRITE_WIDTH * START_SPRITE_RUNNING + SPRITE_WIDTH * spriteCounter, SPRITE_HEIGHT * LINE_SPRITE_RUNNING, SPRITE_WIDTH, SPRITE_HEIGHT); }
                        if (direction.X == MOVE_RIGHT) { Source = new Rectangle((SPRITE_WIDTH * START_SPRITE_RUNNING) + (SPRITE_WIDTH * NB_SPRITE_RUNNING) + SPRITE_WIDTH * spriteCounter, SPRITE_HEIGHT * LINE_SPRITE_RUNNING, SPRITE_WIDTH, SPRITE_HEIGHT); }
                        break;
                    }
                case (State.Jumping):
                    {
                        if (speed.Y >= 0)
                        {
                            if (direction.X == MOVE_LEFT) { Source = new Rectangle(SPRITE_WIDTH * START_SPRITE_JUMPING, SPRITE_HEIGHT * LINE_SPRITE_JUMPING, SPRITE_WIDTH, SPRITE_HEIGHT); }
                            if (direction.X == MOVE_RIGHT) { Source = new Rectangle((SPRITE_WIDTH * START_SPRITE_JUMPING) + (SPRITE_WIDTH * NB_SPRITE_JUMPING), SPRITE_HEIGHT * LINE_SPRITE_JUMPING, SPRITE_WIDTH, SPRITE_HEIGHT); }
                        }
                        if (speed.Y < 0)
                        {
                            if (direction.X == MOVE_LEFT) { Source = new Rectangle(SPRITE_WIDTH * START_SPRITE_FALLING, SPRITE_HEIGHT * LINE_SPRITE_FALLING, SPRITE_WIDTH, SPRITE_HEIGHT); }
                            if (direction.X == MOVE_RIGHT) { Source = new Rectangle((SPRITE_WIDTH * START_SPRITE_FALLING) + (SPRITE_WIDTH * NB_SPRITE_FALLING), SPRITE_HEIGHT * LINE_SPRITE_FALLING, SPRITE_WIDTH, SPRITE_HEIGHT); }
                        }
                        if (superBunbyxWJ)
                        {
                            if (blockedRight)
                            { Source = new Rectangle((SPRITE_WIDTH * START_SPRITE_WALL_GRIPPING), SPRITE_HEIGHT * LINE_SPRITE_WALL_GRIPPING, SPRITE_WIDTH, SPRITE_HEIGHT); }
                            if (blockedLeft)
                            { Source = new Rectangle((SPRITE_WIDTH * (START_SPRITE_WALL_GRIPPING + 1)), SPRITE_HEIGHT * LINE_SPRITE_WALL_GRIPPING, SPRITE_WIDTH, SPRITE_HEIGHT); }
                        }
                        break;
                    }
                case (State.Crounching):
                    {
                        if (direction.X == MOVE_LEFT) { Source = new Rectangle(SPRITE_WIDTH * START_SPRITE_STILL, SPRITE_HEIGHT * LINE_SPRITE_STILL, SPRITE_WIDTH, SPRITE_HEIGHT / 2); }
                        if (direction.X == MOVE_RIGHT) { Source = new Rectangle((SPRITE_WIDTH * START_SPRITE_STILL) + (SPRITE_WIDTH * NB_SPRITE_STILL) , SPRITE_HEIGHT * LINE_SPRITE_STILL, SPRITE_WIDTH, SPRITE_HEIGHT / 2); } 
                        break;
                    }
            }
        }
    }
}
